import Secret from "../assets/secret";
let Util = {};
/**
 * 全局弹窗
 * @param config 弹窗配置
 * @param callback 回调方法
 *
 * config :
 * {
    component:'',//弹窗打开的组件
    title:'',//弹窗名称,默认值:“暂无标题”
    data:{},//传入数据
    curVm:{},//父元素的this
    zIndex:1000,//弹窗层级
    close:(id)=>{},//弹窗关闭时触发，id当前弹窗的id
    class:'',//新增类名
    rightText:'确定',//右边的文字
    color:'',//按钮与文字颜色
    backgroundColor:'#ffffff',//背景颜色
    titleHeight:44,//头部导航栏高度
 * }
 *
 *被打开的页面 props接收：
 *  dialogClose:Function 关闭弹窗的回调 this.dialogClose({data})
 *  parentVm:Function 获取父级实例
 */
Util.open = function (config,callback){
    if(!config.curVm){
        console.log('没有定义父元素的实例');
        return
    }
    config.curVm.$layer.open(config,callback)
}


/**
 * 随机ID
 * <pre>
 *  1.随机产生7为的英文字母。
 *  2.加上当前时间戳。
 * </pre>
 * @memberof Util
 * @returns {String}
 */
Util.randomId=function() {
    let $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    let maxPos = $chars.length;
    let id = '';
    for (let i = 0; i < 7; i++) {
        id += $chars.charAt(Math.floor(Math.random() * maxPos));
    }
    id += (new Date().getTime() + "").substr(7);
    return id;
};



/**
 * 获取对象的数据类型。
 * @memberof Util
 * @param {Object}  obj 对象数据
 * @returns {String}    返回数据得类型。
 */
Util.getObjType=function(obj) {
    let toString = Object.prototype.toString;
    let map = {
        '[object Boolean]': 'boolean',
        '[object Number]': 'number',
        '[object String]': 'string',
        '[object Function]': 'function',
        '[object Array]': 'array',
        '[object Date]': 'date',
        '[object RegExp]': 'regExp',
        '[object Undefined]': 'undefined',
        '[object Null]': 'null',
        '[object Object]': 'object'
    };
    return map[toString.call(obj)];
};
/**
 * 将列表数据转成树形结构数据。
 * @memberof Util
 * @example
 * let ary=[{id:1,name:"a",pid:0},
 {id:2,name:"b",pid:1},
 {id:3,name:"c",pid:1},
 {id:4,name:"d",pid:0},
 {id:5,name:"e",pid:4},
 {id:6,name:"f",pid:4},
 {id:7,name:"i",pid:4},
 {id:8,name:"aa",pid:7},
 {id:9,name:"bb",pid:7}]
 转成下列的结构。
 [{"id": 1,"name": "a","pid": 0,"children": [
 {"id": 2,"name": "b","pid": 1},
 {"id": 3,"name": "c","pid": 1}]
 },
 {"id": 4,"name": "d","pid": 0,
	"children": [{"id": 5,"name": "e","pid": 4},
	{"id": 6,"name": "f","pid": 4},
	{"id": 7,"name": "i","pid": 4,"children": [
		{"id": 8,"name": "aa","pid": 7},
		{"id": 9,"name": "bb","pid": 7}]
	}]
}]
 * @param {Array}   ary       平面数组
 * @param {String}  idKey     主键名
 * @param {String}  pidKey    父键名称
 * @returns {[]}    返回树形数据。
 *
 */
Util.listToTree=function(ary, idKey, pidKey) {
    let obj = {};
    //先对数据进行处理
    //obj[id]=row
    for (let i = 0; i < ary.length; i++) {
        let o = ary[i];
        if (!o.hasOwnProperty("isLeaf")) {
            o.isLeaf = true;
        }
        obj[o[idKey]] = ary[i];
    }
    let rtnAry = [];
    for (let i = 0; i < ary.length; i++) {
        let row = ary[i];
        let id = row[idKey];
        let pid = row[pidKey];

        if (obj[pid] && id != pid) {
            let parent = obj[pid];
            parent.isLeaf = false;
            parent.children = parent.children || [];
            parent.children.push(row);
        } else {
            rtnAry.push(row);
        }
    }
    return rtnAry;
};
/**
 * 将数据转换成树下拉框需要的数据，并进行排序
 * <pre>
 *     树形数据需要的几个固定参数
 *     name: 树形的名称
 *     value: 树形的值
 *     key : 树形的key
 * </pre>
 * @memberof Util
 * @param {Object}  ary     树形的数组
 * @param {String}  name    树形的名称字段
 * @param {String}  value   树形的值
 * @param {String}  key   树形key字段
 * @param {String}  seqField   排序字段
 * @returns {[]}
 */
Util.genTreeData=function(ary, name, value, key,seqField) {
    ary.forEach((item) => {
        let row = {title:item[name], value: item[value], key: item[key]};
        if (item.isLeaf == undefined) {
            let hasChild = item.children && item.children.length > 0;
            row.isLeaf = !hasChild;
        } else {
            row.isLeaf = item.isLeaf;
        }
        Object.assign(item, row);
        if (item.children && item.children.length > 0) {
            this.genTreeData(item.children, name, value, key);
        }
    });
    if(seqField){
        this.sortData(ary, seqField);
    }
};

/**
 * 根据根据属性升序排列
 * @memberof Util
 * @param {Array} data  需要排序的数组
 * @param {String} property 需要排序的属性
 * @returns {*}
 */
Util.sortData=function(data, property) {
    data.sort(function (a, b) {
        return a[property] - b[property];
    });
    return data;
};


/**
 * 将文本写到剪切板。
 * @memberof Util
 * @param {String} text 文本
 */
Util.writeToClipboard=function(text) {
    let textArea = document.querySelector("#temp_textarea");
    if (!textArea) {
        textArea = document.createElement('textarea');
        textArea.setAttribute("id", "temp_textarea");
        // 使text area不在viewport，同时设置不可见
        document.body.appendChild(textArea)
    }
    textArea.value = text;
    textArea.focus()
    textArea.select()
    // 执行复制命令并移除文本框
    document.execCommand('copy');
    textArea.remove();
};

/**
 * 创建VUE3组件。
 * @memberof Util
 * @param {Object}    app     根组件实例
 * @param {String}    name     组件名称
 * @param {String}    template  组件模板
 * @param {String}    js        代码字符串,
 * @param {Array}     aryMixin       需要混入代码的数组,
 * [{
 *     created(){},
 *     methods:{
 *     }
 * }]
 */
Util.createComponent=function(app,name, template,js,aryMixin){
    let tempScript=`let sc=${js};return sc;`;
    let func=new Function(tempScript);
    let script= func();

    let component={
        template:template,
        ...script
    };
    //增加混入。
    if(aryMixin){
        component.mixins=aryMixin;
    }
    //构造组件。
    let rtn=app.component(name,component);
    return rtn;
};

/**
 * 对象克隆。
 * @memberof Util
 * @param {Object}  obj 需要克隆的对象。
 * @returns {null|any}
 */
Util.deepClone=function (obj){
    let cache= new WeakMap();
    function _deepClone(obj) {
        // 如果不是对象或者是null，直接返回
        if (obj == null || typeof obj !== 'object'){
            return obj;
        }
        // 如果已经存在于hash中，直接返回
        if (cache.has(obj)) return cache.get(obj);
        let clone = Array.isArray(obj) ? [] : {};
        cache.set(obj, clone);
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                clone[key] = _deepClone(obj[key]);
            }
        }
        return clone;
    }
    return _deepClone(obj);
}

/**
 * 将数组中的数组按照属性进行排序。
 * @memberof Util
 * @param {Array}   list  数据格式： [{name:"a",age:29},{name:"bb",age:2},{name:"cc",age:19},{name:"dd",age:99}]
 * @param {String}  prop    需要排序的属性
 * @params {Boolean}    asc 是否升序排列
 * @returns {*}
 */
Util.sortArrays=function(list, prop,asc) {
    list.sort(function (a, b) {
        if (a[prop] > b[prop]) {
            return asc? 1: -1;
        }
        if (a[prop] < b[prop]) {
            return asc? -1: 1;
        }
        return 0;
    });
    return list;
};

/**
 * 对单值数据进行排序。
 * @memberof Util
 * @param {Array}   ary     数字组成的数组
 * @param {Boolean} asc     是否升序排列
 * @example
 * let ary=[8,3,5,9,2,1];
 * Util.sort(ary,true);
 * console.info(ary);
 * @returns {*}
 */
Util.sort=function(ary, asc) {
    ary.sort(function (a, b) {
        if (a > b) {
            return asc? 1: -1;
        }
        if (a < b) {
            return asc? -1: 1;
        }
        return 0;
    });
    return ary;
};

/**
 * 防抖函数。
 * @memberof Util
 * @param {Function} func   需要执行的防抖函数
 * @param {Integer}   delay 延迟时间，为毫秒
 * @returns {(function(): void)|*}
 * @example
 * self_.dataChange(val);
 * dataChange: Util.debounce(function (val) {
            let self_ = this;
  }, 500)
 *
 */
Util.debounce=function(func,delay){
    let timer=null;
    return function(){
        let context=this;
        let args=arguments;
        if(timer){
            clearTimeout(timer);
        }
        timer=setTimeout(function (){
            func.apply(context,args);
            timer=null;
        },delay)
    }
};
/**
 * 节流函数。
 * @memberof Util
 * @param {Function}    func    需要执行的节流函数
 * @param {Integer}     interval    多久执行一次时间为毫秒
 * @returns {(function(): void)|*}
 * @example
 * box.addEventListener('mousemove', Util.throttle(function(e){
        console.log(e.pageX)
      }, 2000))
 */
Util.throttle=function(func, interval) {
    let last = 0;
    return function() {
        let args = arguments;
        let now = Date.now();
        if (now - last > interval) {
            func.apply(this, args);
            last = now;
        }
    }
};

/**
 * 将数字转成中文大写。
 * @memberof Util
 * @param {String|Number}  num  数字}
 * @returns {String}
 */
Util.toChineseMoney = function (num) {
    //非数字返回为空
    if(isNaN(num) ){
        return '';
    }
    //将字符串转成数字
    num=Number(num);
    var fraction = ['角', '分'];
    var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
    var unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']];
    var head = num < 0? '欠': '';
    num = Math.abs(num);

    var s = '';

    for (var i = 0; i < fraction.length; i++) {
        s += (digit[Math.floor(num * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
    }
    s = s || '整';
    num = Math.floor(num);

    for (let i = 0; i < unit[0].length && num > 0; i++) {
        var p = '';
        for (var j = 0; j < unit[1].length && num > 0; j++) {
            p = digit[num % 10] + unit[1][j] + p;
            num = Math.floor(num / 10);
        }
        s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
    }

    return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整');
};

/**
 * 格式化日期
 * @memberof Util
 * @example
 * formatDate(new Date(),"yyyy-MM-dd hh:mm:ss")
 * @param {Date}    date    需要格式化的日期
 * @param {String}  format  日期格式(可以使用的格式,yyyy,MM,dd,hh,mm,ss)
 * @returns {*}
 */
Util.formatDate=function(date, format) {
    let o = {
        "M+": date.getMonth() + 1, //月份
        "d+": date.getDate(), //日
        "h+": date.getHours(), //小时
        "m+": date.getMinutes(), //分
        "s+": date.getSeconds(), //秒
        "q+": Math.floor((date.getMonth() + 3) / 3), //季度
        "S": date.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (let k in o){
        if (new RegExp("(" + k + ")").test(format)){
            format = format.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
        }
    }
    return format;
};

/**
 * 将数字格式的时间就行格式化。
 * @memberof Util
 * @param {Number}    time    数字日期
 * @param {String}  format    格式化字符串
 * @returns {*}
 */
Util.formatNumDate=function(time,format){
    let date=new Date(time);
    return this.formatDate(date,format);
};


/**
 * 将JSON转成 地址格式的数据
 * @memberof Util
 * @example
 * let json={a:"1",b:"2"}
 * let json=Util.jsonToUrl(json);
 * //返回为a=1&b=2
 * @param {Object}  json    json对象
 * @returns {string}
 */
Util.jsonToUrl=function(json){
    let ary=[];
    for(let key in json){
        ary.push(key +"=" + json[key]);
    }
    return ary.join("&");
};
/**
 * 将毫秒数转成时间的数据。
 * @memberof Util
 * @param {Number}  time    毫秒数。
 * @returns {String}
 */
Util.getDuration=function(time) {
    var days = time / 1000 / 60 / 60 / 24;
    var daysRound = Math.floor(days);
    var hours = time / 1000 / 60 / 60 - (24 * daysRound);
    var hoursRound = Math.floor(hours);
    var minutes = time / 1000 / 60 - (24 * 60 * daysRound) - (60 * hoursRound);
    var minutesRound = Math.floor(minutes);
    var seconds = Math.floor(time / 1000 - (24 * 60 * 60 * daysRound) - (60 * 60 * hoursRound) - (60 * minutesRound));

    if (daysRound >= 1) {
        return daysRound + '天' + hoursRound + '时' + minutesRound + '分' + seconds + '秒';
    } else if (hoursRound >= 1) {
        return hoursRound + '时' + minutesRound + '分' + seconds + '秒';
    } else if (minutesRound >= 1) {
        return minutesRound + '分' + seconds + '秒';
    }
    return seconds + '秒';
};
/**
 * 输入时间对比和现在的时间差。用当前的时间减去指定的时间，返回时间差。
 * @memberof Util
 * @param {Date} time   指定的时间，一般是过去的时间。
 * @returns {*}
 */
Util.getDurationFromNow=function(time) {
    if (time instanceof Date) {
        time = time.getTime();
    }
    let tmp = (new Date()).getTime() - time;
    let rtn = Util.getDuration(tmp);
    return rtn;
};

/**
 * 将数据转成树形数据。
 * @memberof Util
 * @param {Array}   ary 需要转换大的字符串。
 * @param {String}  id        主键名
 * @param {String}  parentId  父键名
 * @param {String}  children  子节点名
 * @param {String|Number}   rootId    根节点值
 * @returns {JSON} 返回数据格式如下：
 * [{"id": 1,"name": "a","pid": 0,
 "children": [{"id": 2,"name": "b","pid": 1},
 {"id": 3,"name": "c","pid": 1}]
 }]
 */
Util.treeData=function(source, id, parentId, children, rootId='0') {
    id = id || 'id'
    parentId = parentId || 'parentId'
    children = children || 'children'
    const cloneData = this.deepClone(source);
    return cloneData.filter(father => {
        const branchArr = cloneData.filter(child => father[id] === child[parentId])//返回每一项的子级数组
        branchArr.length > 0 ? father[children] = branchArr : delete father[children]//如果存在子级，则给父级添加一个children属性,并赋值
        return father[parentId] === rootId //返回第一层
    })
};
/**
 * 获取窗口的高和宽。
 * @memberof Util
 * @returns {{width: String, height: String}}
 */
Util.getWidthHeight=function() {
    var w = "";
    var h = "";
    //获取窗口宽度
    if (window.innerWidth) {
        w = window.innerWidth;
        h = window.innerHeight;
    } else if ((document.body) && (document.body.clientWidth)) {
        w = document.body.clientWidth;
        h = document.body.clientHeight;
    }
    return {width: w, height: h};
};

/**
 * 替换字符串的 export default 为空，主要是替换 export default ，动态脚本。
 * @memberof Util
 * @param {String} script   需要替换脚本。
 * @returns {String}    返回替换后的脚本。
 */
Util.replaceDefault=function(script){
    if(!script){
        return "{}";
    }
    script = script.replace(/export\s*?default\s*?\{/img, "export default {");
    let index=script.indexOf("export default {");
    if(index!=-1){
        script=script.substring(index + 14);
    }
    return script;
};
/**
 * 判断数据是否为空。
 * @memberof Util
 * @param {Object} val  需要检查的对象。
 */
Util.isEmpty=function(val) {
    if (val == undefined || val == null || val == "" || val == "undefined" || val == "null") {
        return true;
    }
    if (typeof val == "string") {
        return val.trim() == "";
    }
    return false;
};

/**
 * 根据名称获取url参数.
 * @memberof Util
 * @param {String}  name    参数名称
 * @returns {String|null}
 */
Util.getUrlParam=function(name) {
    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [, ""])[1].replace(/\+/g, '%20')) || null
};

/**
 * HEX十六进制颜色值转换为RGB(A)颜色值
 * @memberof Util
 * @param {String}  val 需要将16进制颜色值转换为RGB(A)颜色值
 * @example
 * let color="#f0f0f0";
 * let toColor=Util.hexToRgb(color);
 * console.log(toColor);
 * @returns {String} 返回数据格式如下
 * {"rgb":"rgb(240,240,240)","r":240,"g":240,"b":240}
 */
Util.hexToRgb=function(val){
    //HEX十六进制颜色值转换为RGB(A)颜色值
    // 16进制颜色值的正则
    let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    // 把颜色值变成小写
    let color = val.toLowerCase();
    let result = '';
    if (reg.test(color)) {
        // 如果只有三位的值，需变成六位，如：#fff => #ffffff
        if (color.length === 4) {
            let colorNew = "#";
            for (let i = 1; i < 4; i += 1) {
                colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
            }
            color = colorNew;
        }
        // 处理六位的颜色值，转为RGB
        let colorChange = [];
        for (let i = 1; i < 7; i += 2) {
            colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
        }
        result = "rgb(" + colorChange.join(",") + ")";
        return { rgb: result, r: colorChange[0], g: colorChange[1], b: colorChange[2] };
    } else {
        return { rgb: '-1' };
    }
};

/**
 * 作用：频繁触发$emit时防抖
 * @memberof Util
 * @param {Object}  vm 当前vue实例 this
 * @param {String}  emit 当前需要 emit触发的事件名称
 * @param {Object}  data emit时传出的数据
 * @param {Integer} delay 延迟触发时间,默认400毫秒。
 */
Util.valEmit = function (vm,emit,data,delay=400){
    this.debounce(()=>{
        vm.$emit(emit,data);
    },delay)()
}

/**
 * 传入JSON对象，并删除对象中value的空格，这个可以用于表单提交去除前后空格的功能。
 * @memberof Util
 * @example
 * let json={name:" abc ",age:" 18 "};
 * let rtn= Util.removeSpaces(json);
 * //rtn 返回值为 {name:"abc",age:"18"}
 * @param {JSON} _data JSON对象
 */
Util.removeSpaces=function(_data) {
    //清除字段前后空格；
    for (let key in _data) {
        let item = _data[key];
        if (typeof item == "string" && item != "") {
            _data[key] = item.trim();
        } else if (item && Object.prototype.toString.call(item) == "[object Array]") {
            let _self = this;
            item.forEach(item => {
                _self.removeSpaces(item);
            })
        } else if (item && Object.prototype.toString.call(item) == "[object Object]") {
            this.removeSpaces(item);
        }
    }
}

/**
 * 校验字符串中是否存在中文或者特殊字符
 * @param str 要校验的字符串
 * @returns {boolean} 存在返回true 不存在 返回false
 */
Util.hasChineseOrSpecialChars = function (str){
    const regexChineseAndSpecialChars = /[\u4e00-\u9fff]|[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/g;
    return regexChineseAndSpecialChars.test(str);
}

/**
 *  将字符串转成JSON对象。
 *  主要解决 {text:'abc'} 这种数据使用JSON.parse 出错的问题。
 * @param str
 * @returns {*}
 */
Util.toJson=function(str){
    let fn=new Function("return " + str);
    return fn();
}

/**
 * 加密参数
 * @memberof Util
 */
Util.encryptWord=function(word,encodeUri){
    word=Secret.Encrypt(word);
    if(!encodeUri){
        word=encodeURIComponent(word);
    }
    return word;
};

/**
 * 获取当前语言环境
 * @returns {string}
 */
Util.language = function (){
    return uni.getStorageSync("language");
}

/**
 * 精度计算 超过部分四舍五入
 * 例如：1.756 精度2位的话就是1.76
 * @param number 需要进行精度计算的数字
 * @param precision 精度
 * @returns {number}
 */
Util.roundToPrecision = function (number, precision) {
    let factor = Math.pow(10, precision);
    let tempNumber = number * factor;
    let roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
}

/**
 * 取路由的应用Id
 * @returns {string}
 */
Util.getAppId = function (v) {
    let appId = "";
    // #ifdef H5
    let self =v?v:this;
    if(!self || !self.$route){
        return ''
    }
    if (self.$route.query && self.$route.query.params) {
        //将路由的appId取出来
        if (typeof self.$route.query.params === 'string') {
            appId = JSON.parse(self.$route.query.params).appId
        } else {
            appId = self.$route.query.params.appId;
        }
    }
    if (self.$route.query.appId) {
        appId = self.$route.query.appId;
    }
    // #endif
    // #ifdef APP-PLUS
    appId = uni.getStorageSync("mobileAppId")
    // #endif

    return appId;
}

/**
 * 判断值是否为字符串数字
 * @param date
 * @returns {boolean}
 */
Util.isUnixTimestampString= function (date) {
    if (typeof date == 'number') {
        return true;
    }
    if (typeof date != 'string') {
        return false;
    }
    const unixTimestampPattern = /^\d+$/;
    return unixTimestampPattern.test(date);
}

/**
 * 判断JSON字符串
 * @param value
 * @returns {boolean|any}
 */
Util.checkJSON=function (value) {
    try {
        return JSON.parse(value)
    } catch (e) {
        return false
    }
}


export default Util;
